001 /* 002 * Copyright 2005 Stephen McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.station.server; 020 021 import java.net.URI; 022 import java.net.URL; 023 import java.util.Map; 024 import java.util.Hashtable; 025 import java.util.EventObject; 026 import java.util.EventListener; 027 import java.util.ArrayList; 028 import java.io.File; 029 import java.io.IOException; 030 import java.io.FileInputStream; 031 import java.io.OutputStream; 032 import java.io.FileOutputStream; 033 import java.io.FileNotFoundException; 034 035 import net.dpml.util.Logger; 036 import net.dpml.lang.UnknownKeyException; 037 import net.dpml.lang.DuplicateKeyException; 038 039 import net.dpml.station.ApplicationRegistry; 040 import net.dpml.station.RegistryEvent; 041 import net.dpml.station.RegistryListener; 042 import net.dpml.station.builder.RegistryBuilder; 043 import net.dpml.station.builder.RegistryWriter; 044 import net.dpml.station.info.ApplicationDescriptor; 045 import net.dpml.station.info.RegistryDescriptor; 046 import net.dpml.station.info.RegistryDescriptor.Entry; 047 import net.dpml.station.info.ApplicationRegistryRuntimeException; 048 049 import net.dpml.util.StreamUtils; 050 051 /** 052 * Implements of the application registry within which a set of application profiles 053 * are maintained. 054 * 055 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 056 * @version 1.0.1 057 */ 058 public class RemoteApplicationRegistry extends DefaultModel implements ApplicationRegistry 059 { 060 private final URL m_url; 061 private final Map m_map = new Hashtable(); 062 063 /** 064 * Creation of a new application registry model. 065 * @param logger the assigned logging channel 066 * @param url storage location 067 * @exception Exception if an error occurs 068 */ 069 public RemoteApplicationRegistry( Logger logger, URL url ) throws Exception 070 { 071 super( logger ); 072 073 try 074 { 075 RegistryDescriptor registry = loadRegistryDescriptor( url ); 076 RegistryDescriptor.Entry[] entries = registry.getEntries(); 077 for( int i=0; i<entries.length; i++ ) 078 { 079 RegistryDescriptor.Entry entry = entries[i]; 080 if( null == entry ) 081 { 082 final String error = 083 "Internal error causes by a null registry array entry. Probable " 084 + "cause is an incompatible data source."; 085 throw new ApplicationRegistryRuntimeException( error ); 086 } 087 String key = entry.getKey(); 088 ApplicationDescriptor descriptor = entry.getApplicationDescriptor(); 089 m_map.put( key, descriptor ); 090 } 091 m_url = url; 092 } 093 catch( Exception e ) 094 { 095 final String error = 096 "Unexpected exception raised while loading: " + url; 097 throw new ApplicationRegistryRuntimeException( error, e ); 098 } 099 } 100 101 /** 102 * Return the array of application keys. 103 * @return the application key array 104 */ 105 public String[] getKeys() 106 { 107 synchronized( getLock() ) 108 { 109 return (String[]) m_map.keySet().toArray( new String[0] ); 110 } 111 } 112 113 /** 114 * Return the array of application descriptors. 115 * @return the application descriptor array 116 */ 117 Entry[] getEntries() 118 { 119 synchronized( getLock() ) 120 { 121 String[] keys = getKeys(); 122 ArrayList entries = new ArrayList(); 123 //Entry[] entries = new Entry[ keys.length ]; 124 for( int i=0; i<keys.length; i++ ) 125 { 126 String key = keys[i]; 127 ApplicationDescriptor descriptor = 128 (ApplicationDescriptor) m_map.get( key ); 129 if( null != descriptor ) 130 { 131 Entry entry = new Entry( key, descriptor ); 132 entries.add( entry ); 133 } 134 } 135 return (Entry[]) entries.toArray( new Entry[0] ); 136 } 137 } 138 139 /** 140 * Return the number of application descriptors in the registry. 141 * @return the application descriptor count 142 */ 143 public int getApplicationDescriptorCount() 144 { 145 return m_map.size(); 146 } 147 148 /** 149 * Add an application descriptor to the registry. 150 * @param key the application key 151 * @param descriptor the application descriptor 152 * @exception DuplicateKeyException if the key is already assigned 153 */ 154 public void addApplicationDescriptor( String key, ApplicationDescriptor descriptor ) 155 throws DuplicateKeyException 156 { 157 synchronized( getLock() ) 158 { 159 if( m_map.containsKey( key ) ) 160 { 161 throw new DuplicateKeyException( key ); 162 } 163 m_map.put( key, descriptor ); 164 getLogger().debug( "added application: " + key ); 165 ApplicationDescriptorAddedEvent event = 166 new ApplicationDescriptorAddedEvent( this, descriptor ); 167 enqueueEvent( event ); 168 } 169 } 170 171 /** 172 * Remove an application descriptor from the registry. 173 * @param key the application key 174 * @exception UnknownKeyException if the key is not recognized 175 */ 176 public void removeApplicationDescriptor( String key ) 177 throws UnknownKeyException 178 { 179 synchronized( getLock() ) 180 { 181 if( !m_map.containsKey( key ) ) 182 { 183 throw new UnknownKeyException( key ); 184 } 185 ApplicationDescriptor descriptor = (ApplicationDescriptor) m_map.get( key ); 186 ApplicationDescriptorRemovedEvent event = 187 new ApplicationDescriptorRemovedEvent( this, descriptor ); 188 m_map.remove( key ); 189 getLogger().debug( "removed application: " + key ); 190 enqueueEvent( event ); 191 } 192 } 193 194 /** 195 * Replace an application descriptor within the registry with a supplied descriptor. 196 * @param key the application key 197 * @param descriptor the updated application descriptor 198 * @exception UnknownKeyException if the key is not recognized 199 */ 200 public void updateApplicationDescriptor( String key, ApplicationDescriptor descriptor ) 201 throws UnknownKeyException 202 { 203 synchronized( getLock() ) 204 { 205 m_map.put( key, descriptor ); 206 getLogger().debug( "updated application: " + descriptor ); 207 ApplicationDescriptorAddedEvent event = 208 new ApplicationDescriptorAddedEvent( this, descriptor ); 209 enqueueEvent( event ); 210 } 211 } 212 213 /** 214 * Return an array of all profiles in the registry. 215 * @return the application profiles 216 */ 217 public ApplicationDescriptor[] getApplicationDescriptors() 218 { 219 synchronized( getLock() ) 220 { 221 return (ApplicationDescriptor[]) m_map.values().toArray( new ApplicationDescriptor[0] ); 222 } 223 } 224 225 /** 226 * Retrieve an application profile. 227 * @param key the application profile key 228 * @return the application profile 229 * @exception UnknownKeyException if the key is unknown 230 */ 231 public ApplicationDescriptor getApplicationDescriptor( String key ) 232 throws UnknownKeyException 233 { 234 synchronized( getLock() ) 235 { 236 if( !m_map.containsKey( key ) ) 237 { 238 throw new UnknownKeyException( key ); 239 } 240 return (ApplicationDescriptor) m_map.get( key ); 241 } 242 } 243 244 /** 245 * Flush the state of the server to external storage. 246 * @exception IOException if an I/O error occurs 247 */ 248 public void flush() throws IOException 249 { 250 synchronized( getLock() ) 251 { 252 if( null == m_url ) 253 { 254 return; 255 } 256 257 File file = File.createTempFile( "dpml-station", ".xml" ); 258 getLogger().debug( "writing to temp file: " + file ); 259 file.createNewFile(); 260 FileOutputStream output = new FileOutputStream( file ); 261 Entry[] entries = getEntries(); 262 RegistryDescriptor descriptor = new RegistryDescriptor( entries ); 263 RegistryWriter writer = new RegistryWriter(); 264 writer.writeRegistryDescriptor( descriptor, output, "" ); 265 FileInputStream input = new FileInputStream( file ); 266 OutputStream dest = m_url.openConnection().getOutputStream(); 267 StreamUtils.copyStream( input, dest, true ); 268 getLogger().debug( "updated registry: " + m_url ); 269 } 270 } 271 272 /** 273 * Add a depot content change listener. 274 * @param listener the registry change listener to add 275 */ 276 public void addRegistryListener( RegistryListener listener ) 277 { 278 super.addListener( listener ); 279 } 280 281 /** 282 * Add a registry change listener. 283 * @param listener the registry change listener to add 284 */ 285 public void removeRegistryListener( RegistryListener listener ) 286 { 287 super.removeListener( listener ); 288 } 289 290 /** 291 * Proces a registry event. 292 * @param event the event top process 293 */ 294 protected void processEvent( EventObject event ) 295 { 296 if( event instanceof RegistryEvent ) 297 { 298 processRegistryEvent( (RegistryEvent) event ); 299 } 300 else 301 { 302 final String error = 303 "Event class not recognized: " 304 + event.getClass().getName(); 305 throw new IllegalArgumentException( error ); 306 } 307 } 308 309 /** 310 * Return a string representation of the registy model. 311 * @return the string value 312 */ 313 public String toString() 314 { 315 return "[registry]"; 316 } 317 318 private RegistryDescriptor loadRegistryDescriptor( URL url ) throws Exception 319 { 320 if( null == url ) 321 { 322 return new RegistryDescriptor( new Entry[0] ); 323 } 324 else 325 { 326 RegistryBuilder builder = new RegistryBuilder(); 327 try 328 { 329 Object object = builder.build( new URI( url.toString() ) ); 330 if( object instanceof RegistryDescriptor ) 331 { 332 return (RegistryDescriptor) object; 333 } 334 else 335 { 336 final String error = 337 "The object returned from the uri [" 338 + url 339 + "] of the class [" 340 + object.getClass().getName() 341 + "] is not an instance of " 342 + RegistryDescriptor.class.getName() 343 + "."; 344 throw new IllegalArgumentException( error ); 345 } 346 } 347 catch( FileNotFoundException e ) 348 { 349 return new RegistryDescriptor( new Entry[0] ); 350 } 351 } 352 } 353 354 private void processRegistryEvent( RegistryEvent event ) 355 { 356 EventListener[] listeners = super.listeners(); 357 for( int i=0; i<listeners.length; i++ ) 358 { 359 EventListener listener = listeners[i]; 360 if( listener instanceof RegistryListener ) 361 { 362 RegistryListener rl = (RegistryListener) listener; 363 if( event instanceof ApplicationDescriptorAddedEvent ) 364 { 365 try 366 { 367 rl.profileAdded( event ); 368 } 369 catch( Throwable e ) 370 { 371 final String error = 372 "RegistryListener profile addition notification error."; 373 getLogger().error( error, e ); 374 } 375 } 376 else if( event instanceof ApplicationDescriptorRemovedEvent ) 377 { 378 try 379 { 380 rl.profileRemoved( event ); 381 } 382 catch( Throwable e ) 383 { 384 final String error = 385 "RegistryListener profile removed notification error."; 386 getLogger().error( error, e ); 387 } 388 } 389 } 390 } 391 } 392 393 /** 394 * ApplicationDescriptorAddedEvent. 395 */ 396 static class ApplicationDescriptorAddedEvent extends RegistryEvent 397 { 398 /** 399 * Creation of a new ProfileAddedEvent. 400 * @param source the source registry 401 * @param descriptor the application descriptor that was added 402 */ 403 public ApplicationDescriptorAddedEvent( 404 ApplicationRegistry source, ApplicationDescriptor descriptor ) 405 { 406 super( source, descriptor ); 407 } 408 } 409 410 /** 411 * ApplicationDescriptorRemovedEvent. 412 */ 413 static class ApplicationDescriptorRemovedEvent extends RegistryEvent 414 { 415 /** 416 * Creation of a new ProfileRemovedEvent. 417 * @param source the source registry 418 * @param descriptor the application descriptor that was removed 419 */ 420 public ApplicationDescriptorRemovedEvent( ApplicationRegistry source, ApplicationDescriptor descriptor ) 421 { 422 super( source, descriptor ); 423 } 424 } 425 }